#include "lib_qbar.h"

/**
* States
*/ 

SleepState::SleepState(QB* qb) : State(qb)
{
	_effect = PulsateEffect(qb, 8, 32, SLEEP_BASE_TIME + random(0, SLEEP_RANDOM_TIME));
}

void SleepState::enter() {
	State::enter();
	_effect.startEffect();
	_qb->setHue(SLEEP_HUE);
	_qb->setSaturation(255);
	_qb->setValue(0);
	_qb->playAudioFile(4);
	_startSleep = millis();
	_qb->resetBlinkM();
}

State* SleepState::execute(){
	_effect.updateEffect();
	if (millis() - _startSleep < SLEEP_START_TIME) { return this; }
	return checkTransitions();
}

void SleepState::exit() {
	_effect.stopEffect();
	if (_qb->isPlayingAudio()) { _qb->stopPlaying(); }
}

ScriptSelectState::ScriptSelectState(QB* qb, SoundState* senseState, ColorSmackState* createState, PuzzleState* puzzleState) : State(qb) {
	_senseState  = senseState;
	_createState = createState;
	_puzzleState = puzzleState;
}

void ScriptSelectState::enter() {
	State::enter();
	int prevScript = _qb->getScript();
	switch (prevScript) {
		case 0:             _qb->setScript(SCRIPT_SENSE);  _qb->broadcastCommand('0', COMMAND_SCRIPT_SENSE); break;
		case SCRIPT_SENSE:  _qb->setScript(SCRIPT_CREATE); _qb->broadcastCommand('0', COMMAND_SCRIPT_CREATE); break;
		case SCRIPT_CREATE: _qb->setScript(SCRIPT_PUZZLE); _qb->broadcastCommand('0', COMMAND_SCRIPT_PUZZLE); break;
		case SCRIPT_PUZZLE: _qb->setScript(SCRIPT_SENSE);  _qb->broadcastCommand('0', COMMAND_SCRIPT_SENSE); break;
	}
}

State* ScriptSelectState::execute(){ 
	int script = _qb->getScript();
	State* next;
	switch (script) {
		case SCRIPT_SENSE:  next = _senseState;  break;
		case SCRIPT_CREATE: next = _createState; break;
		case SCRIPT_PUZZLE: next = _puzzleState; break;
	}

	next->enter();
	return next;
}

SoundState::SoundState(QB* qb) : State(qb) {}

void SoundState::enter() {
	State::enter();
	_qb->setScript(SCRIPT_SENSE);
	_qb->setValue(0);
	_nextPlay = millis() + random(SOUND_MIN_WAIT, SOUND_MAX_WAIT);
	_intensity = 64;

	//For uplight
	_qb->setValue(0);
	_qb->setSaturation(255);
	_previousSide = 'D';
}

State* SoundState::execute(){ 
	if (_qb->pendingCommandIs(COMMAND_SOUND_PLAYED)) { _nextPlay = millis() + random(SOUND_MIN_WAIT, SOUND_MAX_WAIT); }

	if (millis() > _nextPlay) {
		switch (random(0,11)) {
			#ifdef USESOUND
			case 0: _qb->playAudioFile(0);  break; // cat
			case 1: _qb->playAudioFile(12); break; // sheep
			case 2: _qb->playAudioFile(40); break; //submarine
			case 3: _qb->playAudioFile(30); break; // owl
			case 4: _qb->playAudioFile(19); break; // hello
			case 5: _qb->playAudioFile(11); break; // Plystring
			case 6: _qb->playAudioFile(22); break; // Jazz
			case 7: _qb->playAudioFile(26); break; // Flyby
			case 8: _qb->playAudioFile(27); break; // Kråke
			case 9: _qb->playAudioFile(41); break; // Fløytenes
			case 10: _qb->playAudioFile(42); break; // Glass
			case 11: _qb->playAudioFile(54); break; // Glass
			#endif
		}
		_intensity = 255;
		_nextPlay = millis() + random(SOUND_MIN_WAIT, SOUND_MAX_WAIT); 
	}


	//Uplight effect i soundstate
	_qb->getMotionSensor()->update();
	char dside = this->_qb->getMotionSensor()->getDownside();
	char lside = '0'; //lside betyr lightside
	switch (dside) {
		case 'D': lside = 'U'; break;
		case 'U': lside = 'D'; break;
		case 'N': lside = 'S'; break;
		case 'S': lside = 'N'; break;
		case 'W': lside = 'E'; break;
		case 'E': lside = 'W'; break;
	}

	if (_previousSide != lside) {
		if (_previousSide == '0') {
		_qb->setValue(0);
		}
		else {
			_qb->setValue(0, _previousSide);
		}
	
		if (lside == '0') {
			_qb->setHue(random(0, 360));
			_qb->setValue(255);
			_previousSide = lside;
		}
		else {
			_qb->setHue(random(0, 360), lside);
			_qb->setValue(_intensity, lside);
			_previousSide = lside;
		}
	}
	if (_qb->isPlayingAudio()) {
		_intensity = 255;
	}

	_qb->setValue(_intensity, lside);
	if (_intensity > 64) { _intensity--; }
	return checkTransitions();
}

PlayState::PlayState(QB* qb) : State(qb) {}

void PlayState::enter() {
	State::enter();
	_agitation = PLAY_AGITATION_TRESHOLD;
	_hue = random(0, 360);
	_qb->setHue(_hue);
	_qb->setSaturation(255);
	_qb->setValue(_agitation);
	_lastSide = 'U';
	_startCrazy = -PLAY_AGITATION_TIME;
}

State* PlayState::execute(){ 
	if (millis() - _startCrazy < PLAY_AGITATION_TIME) {
		_qb->setHue(360 - _hue);
		_qb->setValue(0);
		_qb->vibrate(255);
		if (millis() - _lastLightChange > PLAY_LIGHT_SWITCH_TIME) {
			switch (_lastSide) {
				case 'U': _lastSide = 'D'; _qb->setValue(255, _lastSide); break;
				case 'D': _lastSide = 'N'; _qb->setValue(255, _lastSide); break;
				case 'N': _lastSide = 'S'; _qb->setValue(255, _lastSide); break;
				case 'S': _lastSide = 'W'; _qb->setValue(255, _lastSide); break;
				case 'W': _lastSide = 'E'; _qb->setValue(255, _lastSide); break;
				case 'E': _lastSide = 'U'; _qb->setValue(255, _lastSide); break;
			}
			_lastLightChange = millis();
		}
	}
	else {
		if (_qb->pendingCommandIs(COMMAND_AGITATED)) { 
			_qb->playAudioFile(2);
			_agitation += PLAY_INCREASE; 
		}
		if (_agitation > PLAY_AGITATION_TRESHOLD) { _agitation = PLAY_AGITATION_TRESHOLD; }
		if (millis() - _lastReduction > PLAY_REDUCTION_TIME && _agitation > PLAY_AGITATION_MIN) { _lastReduction = millis(); _agitation--; }
		_qb->vibrate(0);
		_qb->setValue(_agitation);
		_qb->setHue(_hue);
		MotionSensor* m = _qb->getMotionSensor();
		if (m->getDelta() > PLAY_DELTA_TRESHOLD) { _agitation += _agitation < PLAY_AGITATION_TRESHOLD / 3 ? PLAY_INCREASE : (_agitation < PLAY_AGITATION_TRESHOLD / 2 ? PLAY_INCREASE / 2 : PLAY_INCREASE / 3); }
		if (_agitation > PLAY_AGITATION_TRESHOLD) { 
			_startCrazy = millis();
			_agitation = PLAY_AGITATION_TRESHOLD;
			_qb->broadcastCommand('0', COMMAND_AGITATED);
		}
	}
	return checkTransitions();
}

ColorSmackState::ColorSmackState(QB* qb) : State(qb) {}

void ColorSmackState::enter() {
	State::enter();
	_hues[0] = 0;
	_hues[1] = 60;
	_hues[2] = 120;
	_hues[3] = 180;
	_hues[4] = 240;
	_hues[5] = 300;
	
	_currHue = 0; //random(0, 6);
	_targetHue = _currHue;
	_hue = _hues[_currHue];
	_qb->setValue(255);
	_qb->setSaturation(255);
	_qb->setHue(_hues[0], 'U');
	_qb->setHue(_hues[1], 'W');
	_qb->setHue(_hues[2], 'N');
	_qb->setHue(_hues[3], 'D');
	_qb->setHue(_hues[4], 'E');
	_qb->setHue(_hues[5], 'S');
	_lastChange = millis();
	_qb->setScript(SCRIPT_CREATE);
}

State* ColorSmackState::execute(){ 
	MotionSensor* m = _qb->getMotionSensor();
	if (millis() - _lastChange > SMACK_DELAY && m->getDelta() > SMACK_POWER) {
		_targetHue++;
		if (_targetHue > 5) { _targetHue = 0; }
		_lastChange = millis();
	}
	
	if (_currHue != _targetHue) {
		_hue += 2;
		if (_hue > 359) { _hue = 0; }
		if (_hue == _hues[_targetHue]) { _currHue = _targetHue; }
		_qb->setHue(_hue);
	}

	return checkTransitions();
}

PuzzleState::PuzzleState(QB* qb) : State(qb) {}

void PuzzleState::enter() {
	State::enter();
	_c = random(0, 2) == 0 ? PUZZLE_COLOR_1 : PUZZLE_COLOR_2;
	_qb->setHue(_c);
	_qb->setValue(255);
	_lastChange = millis();
	_qb->setScript(SCRIPT_PUZZLE);
}

State* PuzzleState::execute(){ 
	if (_qb->pendingCommandIs(COMMAND_PUZZLE)) {
		_c = _c == PUZZLE_COLOR_1 ? PUZZLE_COLOR_2 : PUZZLE_COLOR_1;
		_qb->setHue(_c);
		_qb->playAudioFile(7);
	}
	MotionSensor* m = _qb->getMotionSensor();

	if (millis() - _lastChange > SMACK_DELAY && m->getDelta() > SMACK_POWER) {
		_lastChange = millis();
		switch (_qb->getQbId()) { 
			case 'A': _qb->broadcastCommand('J', COMMAND_PUZZLE); _qb->broadcastCommand('D', COMMAND_PUZZLE); break;
			case 'B': _qb->broadcastCommand('K', COMMAND_PUZZLE); _qb->broadcastCommand('E', COMMAND_PUZZLE); break;
			case 'C': _qb->broadcastCommand('L', COMMAND_PUZZLE); _qb->broadcastCommand('F', COMMAND_PUZZLE); break;
			case 'D': _qb->broadcastCommand('M', COMMAND_PUZZLE); _qb->broadcastCommand('G', COMMAND_PUZZLE); break;
			case 'E': _qb->broadcastCommand('N', COMMAND_PUZZLE); _qb->broadcastCommand('H', COMMAND_PUZZLE); break;
			case 'F': _qb->broadcastCommand('O', COMMAND_PUZZLE); _qb->broadcastCommand('I', COMMAND_PUZZLE); break;
			case 'G': _qb->broadcastCommand('P', COMMAND_PUZZLE); _qb->broadcastCommand('J', COMMAND_PUZZLE); break;
			case 'H': _qb->broadcastCommand('Q', COMMAND_PUZZLE); _qb->broadcastCommand('K', COMMAND_PUZZLE); break;
			case 'I': _qb->broadcastCommand('R', COMMAND_PUZZLE); _qb->broadcastCommand('L', COMMAND_PUZZLE); break;
			case 'J': _qb->broadcastCommand('A', COMMAND_PUZZLE); _qb->broadcastCommand('M', COMMAND_PUZZLE); break;
			case 'K': _qb->broadcastCommand('B', COMMAND_PUZZLE); _qb->broadcastCommand('N', COMMAND_PUZZLE); break;
			case 'L': _qb->broadcastCommand('C', COMMAND_PUZZLE); _qb->broadcastCommand('O', COMMAND_PUZZLE); break;
			case 'M': _qb->broadcastCommand('D', COMMAND_PUZZLE); _qb->broadcastCommand('P', COMMAND_PUZZLE); break;
			case 'N': _qb->broadcastCommand('E', COMMAND_PUZZLE); _qb->broadcastCommand('Q', COMMAND_PUZZLE); break;
			case 'O': _qb->broadcastCommand('F', COMMAND_PUZZLE); _qb->broadcastCommand('R', COMMAND_PUZZLE); break;
			case 'P': _qb->broadcastCommand('G', COMMAND_PUZZLE); _qb->broadcastCommand('A', COMMAND_PUZZLE); break;
			case 'Q': _qb->broadcastCommand('H', COMMAND_PUZZLE); _qb->broadcastCommand('B', COMMAND_PUZZLE); break;
			case 'R': _qb->broadcastCommand('I', COMMAND_PUZZLE); _qb->broadcastCommand('C', COMMAND_PUZZLE); break;
			default : _qb->broadcastCommand('0', COMMAND_PUZZLE);
		}
	}

	return checkTransitions();
}

IdentifyState::IdentifyState(QB* qb) : State(qb) {}

void IdentifyState::enter() {
	State::enter();
	char id = _qb->getQbId();
	boolean red    = id == 'A' || id == 'B' || id == 'C';
	boolean yellow = id == 'D' || id == 'E' || id == 'F';
	boolean green  = id == 'G' || id == 'H' || id == 'I';
	boolean cyan   = id == 'J' || id == 'K' || id == 'L';
	boolean blue   = id == 'M' || id == 'N' || id == 'O';
	boolean purple = id == 'P' || id == 'Q' || id == 'R';

	boolean onW = id == 'A' || id == 'D' || id == 'G' || id == 'J' || id == 'M' || id == 'P';
	boolean onN = id == 'B' || id == 'E' || id == 'H' || id == 'K' || id == 'N' || id == 'Q';
	boolean onE = id == 'C' || id == 'F' || id == 'I' || id == 'L' || id == 'O' || id == 'R';

	_qb->setHue(red ? 0 : (yellow ? 60 : (green ? 120 : (cyan ? 180 : (blue ? 240 : 300)))));
	_qb->setSaturation(255);
	_qb->setValue(onW ? 255 : 0, 'W');
	_qb->setValue(onN ? 255 : 0, 'N');
	_qb->setValue(onE ? 255 : 0, 'E');

	_qb->setValue(255, 'U');
	_qb->setSaturation(0, 'U');  
	_qb->setValue(255, 'S');
	_qb->setSaturation(0, 'S');
	_qb->setValue(0, 'D');
}

State* IdentifyState::execute(){ 
	return checkTransitions();
}


/**
* Transitions
*/


	CalmTransition::CalmTransition(QB* qb, int timeout) : Transition()
{
	_qb = qb;
	_lastMove = millis();
	_timeout = timeout;
}

void CalmTransition::init() {
	_lastMove = millis();
}

boolean CalmTransition::evaluate()
{
	if (_qb->getMotionSensor()->getDelta() > 10) { _lastMove = millis(); }
	return millis() - _lastMove > _timeout;
}

ClapTransition::ClapTransition(int claps, QB* qb) : Transition()
{
	_qb    = qb;
	_claps = claps;
}

boolean ClapTransition::evaluate() {
	Microphone* mic = _qb->getMicrophone();
	return mic->getTimeSinceLastClap() > 200 && mic->getNumClaps() == _claps && !_qb->getMotionSensor()->hasMoved();
}

TimeTransition::TimeTransition(long millis) : Transition()
{
	_time = millis;
	_start = 0;
}

void TimeTransition::init() {
	_start = millis();
}

boolean TimeTransition::evaluate() {
	return millis() - _start > _time;
}

MoveTransition::MoveTransition(int treshold, QB* qb) : Transition()
{
	_treshold = treshold;
	_qb       = qb;
}

void MoveTransition::init() {

}

boolean MoveTransition::evaluate() {
	MotionSensor* m = _qb->getMotionSensor();
	return m->getDelta() > _treshold;
}


CountTransition::CountTransition(int num, Countable* countable) : Transition()
{
	_countable = countable;
	_num       = num;
}

boolean CountTransition::evaluate() {
	return _countable->getCount() >= _num;
}


CommandTransition::CommandTransition(QB* qb, int command) : Transition()
{
	_qb      = qb;
	_command = command;
}

boolean CommandTransition::evaluate() {
	return _qb->pendingCommandIs(_command);
}